<?

/**
 * Klasa ObjectWatcher - implementacja wzorca Identity Map i Unit of Work
 * @param array $all - przechowuje obiekty dodane do mapy, jesli uzytkownik zazada danego obiektu to w pierwszej kolejnosci obecnosc jego jest sprawdzana w tej tablicy
 * jesli istnieje to zwraca obiekt z mapy, jesli nie to jest wykonywane zapytanie do bazy i jego utworzenie
 * @param array $dirty - przechowuje informacje o obiektach, na ktorych dokonano modyfikacji przez uzycie metody setParam - Unit of Work 
 * - wykorzystywane przy utrwalaniu obiektow w bazie przez destruktor klasy
 * @param array $new - przechowuje liste obiektow utworzonych bez identyfikatora, na podstawie tej tablicy destruktor klasy utrwala stworzone obiekty modelu w bazie danych - Unit of Work
 * @param array $delete - analogicznie do new i dirty - delete nie jest zaimplementowane
 * @param ObjectWatcher $instance - przechowuje instancje siebie - singleton
 */
class ObjectWatcher{
	private $all = array();
	private $dirty = array();
	private $new = array();
	private $deleted = array();
	private static $instance;
	
	/**
	 * Konstruktor prywatny
	 *
	 */
	private function __construct(){}
	
	/**
	 * Wzorzec singleton - tworzy obiekt klasy badz zwraca jesli zostal juz utworzony
	 *
	 * @return ObjectWatcher $instance
	 */
	static function instance(){
		if (!self::$instance){
			self::$instance = new ObjectWatcher();
		}
		return self::$instance;
	}
	
	/**
	 * Metoda globalKey - sluzy do wytworzenia unikalnego klucza - na podstawie ktorego bedzie trzymany obiekt modelu w tablicy $all
	 *
	 * @param DomainModel $obj
	 * @return string $key
	 */
	function globalKey(DomainModel $obj){
		$key = get_class($obj).'.'.$obj->getId();
		return $key;
	}
	
	/**
	 * Metoda statyczna add - sluzy do dodawania obiektow modelu do mapy tozsamosci
	 *
	 * @param DomainModel $obj
	 */
	static function add(DomainModel $obj){
		//debug($obj);
		$inst = self::instance();
		$inst->all[$inst->globalKey($obj)] = $obj;
	}
	
	/**
	 * Metoda statyczna exists - sprawdza istnienie danego obiektu na mapie tozsamosci na podstawie nazwy klasy modelu i identyfikatora
	 *
	 * @param string $classname
	 * @param integer $id
	 * @return DomainModel $obj || boolean false
	 */
	static function exists($classname, $id){
		$inst = self::instance();
		//debug($inst->all);
		$key = $classname.'.'.$id;
		if (isset($inst->all[$key]))
			return $inst->all[$key];
		else 
			return false;
	}
	
	/**
	 * Metoda statyczna addDirty - sluzy do dodawania informacji o obiektach zmodyfikowanych - brudnych - do tablicy $dirty - Unit of Work
	 *
	 * @param DomainModel $obj
	 */
	static function addDirty(DomainModel $obj){
		$inst = self::instance();
		if (!isset($inst->new[$inst->globalKey($obj)])){
			$inst->dirty[$inst->globalKey($obj)] = $obj;
		}
	}
	
	/**
	 * Metoda statyczna addDeleted - sluzy do dodawania informacji o obiektach do usuniecia do tablicy $deleted - Unit of Work
	 *
	 * @param DomainModel $obj
	 */
	static function addDeleted(DomainModel $obj){
		$inst = self::instance();
		$inst->deleted[$inst->globalKey($obj)] = $obj;
	}
	
	/**
	 * Metoda statyczna addNew - sluzy do rejestrowania informacji o obiektach nowo powstalych - Unit of Work 
	 *
	 * @param DomainModel $obj
	 */
	static function addNew(DomainModel $obj){
		$inst = self::instance();
		$inst->new[$inst->globalKey($obj)] = $obj;
	}
	
	/**
	 * Metoda statyczna checkIsNew - sluzy do sprawdzenia informacji o istnieniu obiektu w tablicy new do zapisania w bazie- Unit of Work 
	 *
	 * @param DomainModel $obj
	 */
	static function checkIsNew($obj){
		$inst = self::instance();
		if (isset($inst->new[$inst->globalKey($obj)]))
			return true;
		else
			return false;
	}

	/**
	 * Metoda statyczna addClean - sluzy do usuwania informacji o modyfikacjach / utworzniu w obiekcie modelu - sztuczne zatrzymanie utrwalenia w bazie danych - Unit of Work
	 *
	 * @param DomainModel $obj
	 */
	static function addClean(DomainModel $obj){
		$self = self::instance();
		unset($self->dirty[$self->globalKey($obj)]);
		unset($self->deleted[$self->globalKey($obj)]);
		unset($self->new[$self->globalKey($obj)]);
	}
	
	/**
	 * Metoda statyczna flushOperations
	 *
	 * @param DomainModel $obj
	 */
	static function flushOperations(){
		$self = self::instance();
		$self->execOperations();
	}
	
	/**
	 * Metoda execOperations - metoda wywolywana przez destruktor obiektu ObjectWatcher - utrwala w bazie danych informacje o obiektach zdefiniowanych w tablicach $new i $dirty
	 *
	 */
	function execOperations(){
		foreach ($this->dirty as $key => $obj){
			if (!$obj->getIsGhost())
				try {
					$obj->getFinder(get_class($obj))->update($obj);
				}catch (Exception $e){
					echo ('Object Watcher Error: UPDATE');
					debug($obj);
	
				}
		} 
		
		foreach ($this->new as $key => $obj){
			//debug($obj);
			try {
				$obj->getFinder(get_class($obj))->insert($obj);
			}catch (Exception $e){
				echo ('Object Watcher Error: INSERT');
				debug($obj);

			}
		}
		
		foreach ($this->deleted as $key => $obj){
			try {
				$obj->getFinder(get_class($obj))->delete($obj);
			}catch (Exception $e){
				echo ('Object Watcher Error: DELETE');
				debug($obj);

			}
		}
		//debug($this->new);
		$this->dirty = array();
		$this->new = array();
		$this->deleted = array();
		
	}
	
	/**
	 * Destruktor - uruchamia proces utrwalania zmian w bazie danych - Unit of Work - metoda execOperations
	 *
	 */
	function __destruct(){
		$this->execOperations();
	}
}

?>